using System;
using System.Collections.Generic;
using System.Text;

using Mono.Cecil;
using Mono.Cecil.Cil;

using CILUtil;

namespace CILFlow
{
    class Entity
    {
        public static object GetEntity(MethodDefinition md, Instruction i)
        {
            object operand = i.Operand == null ? GetOperand(md, i) : i.Operand;
            if( IsOfInterest( operand ) )
                return operand;
            return null;
        }

        public static object GetOperand(MethodDefinition md, Instruction i)
        {
            object operand = GetParameterOperand(md, i);
            if (operand != null)
                return operand;
            return GetLocalOperand(md, i);
        }
        public static object GetLocalOperand(MethodDefinition md, Instruction i)
        {
            int index = -1;
            switch (i.OpCode.Code)
            {
                case Code.Ldloc_0:
                case Code.Stloc_0:
                    index = 0;
                    break;
                case Code.Ldloc_1:
                case Code.Stloc_1:
                    index = 1;
                    break;
                case Code.Ldloc_2:
                case Code.Stloc_2:
                    index = 2;
                    break;
                case Code.Ldloc_3:
                case Code.Stloc_3:
                    index = 3;
                    break;
            }
            if (index != -1)
            {
                return md.Body.Variables[index];
            }
            return null;
        }
        public static object GetParameterOperand(MethodDefinition md, Instruction i)
        {
            int index = -1;
            switch (i.OpCode.Code)
            {
                case Code.Ldarg_0:
                    index = 0;
                    break;
                case Code.Ldarg_1:
                    index = 1;
                    break;
                case Code.Ldarg_2:
                    index = 2;
                    break;
                case Code.Ldarg_3:
                    index = 3;
                    break;
            }
            if (index != -1)
            {
                if (md.HasThis && index == 0)
                {
                    //i.Operand = md.This;
                    // `This' is useless information to flow.
                }
                else
                {
                    if (md.HasThis)
                    {
                        index--;
                    }
                    return md.Parameters[index];
                }
            }
            return null;
        }

        public static bool IsOfInterest(object subject)
        {
            if (subject == null)
                return false;
            Type s = subject.GetType();
            foreach (Type t in OfInterest)
            {
                if (t == s)
                    return true;
            }
            return false;
        }

        public static Type[] OfInterest = new Type[] {typeof(ParameterDefinition),
                                                typeof(ParameterReference),
                                                typeof(VariableDefinition),
                                                typeof(VariableReference),
                                                typeof(FieldDefinition),
                                                typeof(FieldReference),
                                                typeof(PropertyDefinition),
                                                typeof(PropertyReference),
                                                typeof(MethodDefinition),
                                                typeof(MethodReference),
                                                typeof(TypeDefinition),
                                                typeof(TypeReference),
                                                typeof(EventDefinition),
                                                typeof(EventReference),
                                                typeof(FunctionPointerType),
                                                typeof(GenericParameter),
                                                typeof(String)
        };
    }
}
